#include "../include/requesthandle.h"
#include "../include/macro_file_type.h"
#include "../include/macro_str.h"
#include "../include/macro_report.h"
#include "../include/macro_signal.h"
#include "../include/node.h"
#include "../include/extfsdll.h"
#include "../include/filearray.h"
#include "../include/events.h"
#include "../include/errorhandle.h"
#include "../include/progress.h"
#include "../include/parameters.h"
#include "../include/report.h"
#include <windows.h>
#include <QString>
#include <QtGui/QApplication>
#include <QDir>
#include <QFile>
void RequestHandle::cancelCopy()
{
	ExtFsDLL::suspendCopy();
}

void RequestHandle::needNodeArray(const Node& dir,uint32 flag)
{
	const NodeArray* node_array=cache.findNodeArray(dir.path(),flag);
	if(node_array==NULL)
	{
		node_array=getNewNodeArrayFor(dir);
		if(node_array==NULL)
		{
			ErrorReport error_report;
			ExtFsDLL::getErrorReport(error_report);
			emit error(error_report,REQUESTHANDLE);
			return;
		}
		cache.cacheNewNodeArray(node_array,flag);
	}
	emit sendNodeArray(dir,node_array,flag);
}
void RequestHandle::needNodeArray(const QString& path,uint32 flag)
{
	const NodeArray* node_array=cache.findNodeArray(path,flag);
	if(node_array==NULL)
	{
		node_array=getNewNodeArrayFor(path);
		if(node_array==NULL)
		{
			ErrorReport error_report;
			ExtFsDLL::getErrorReport(error_report);
			emit error(error_report,REQUESTHANDLE);
			return;
		}
		cache.cacheNewNodeArray(node_array,flag);
	}
	emit sendNodeArray(Node(path,0),node_array,flag);
}
const NodeArray* RequestHandle::getNewNodeArrayFor(const Node& dir) const
{
	uint32 disk_index=dir.path().section(CH_SLASH,1,1).toInt();
	uint32 fs_index=dir.path().section(CH_SLASH,2,2).toInt();
	const ParaForScanDirByInodeNum para(disk_index,fs_index,dir.inodeNum());
	FileArray file_array(ExtFsDLL::scanDir(para));
	if(file_array.isEmpty())
		return NULL;
	NodeArray* node_array=new NodeArray(file_array);
	node_array->addPath(dir.path());
	return node_array;
}
const NodeArray* RequestHandle::getNewNodeArrayFor(const QString& path) const
{
	uint32 disk_index=path.section(CH_SLASH,1,1).toInt();
	uint32 fs_index=path.section(CH_SLASH,2,2).toInt();
	QString real_path=STR_SLASH+path.section(CH_SLASH,3,-1);
	const ParaForScanDirByPath para(disk_index,fs_index,real_path);
	FileArray file_array(ExtFsDLL::scanDir(para));
	if(file_array.isEmpty())
		return NULL;
	NodeArray* node_array=new NodeArray(file_array);
	node_array->addPath(path);
	return node_array;
}
void RequestHandle::needToCopyFile(QList<QTreeWidgetItem*>& files,const FsIndex& fs_index,const QString& from_path,const QString& to_path)
{
	if(!copy_thread.is_finished)
	{
		copy_thread.lock_for_files.lock();
		if(!copy_thread.is_finished)
		{
			copy_thread.files.reserve(copy_thread.files.size()+files.size());
			for(uint32 i=0;i<files.size();i++)
				copy_thread.files.append(ParaForCopyFile(
				fs_index,
				static_cast<Node*>(files[i])->inodeNum(),
				from_path,
				to_path+static_cast<Node*>(files[i])->name(),
				static_cast<Node*>(files[i])->type()));
			copy_thread.lock_for_files.unlock();
			return;
		}
		copy_thread.lock_for_files.unlock();
	}
	copy_thread.wait();
	copy_thread.files.clear();
	copy_thread.is_stop=false;
	copy_thread.files.reserve(files.size());
	for(uint32 i=0;i<files.size();i++)
		copy_thread.files.append(ParaForCopyFile(
		fs_index,
		static_cast<Node*>(files[i])->inodeNum(),
		from_path,
		to_path+static_cast<Node*>(files[i])->name(),
		static_cast<Node*>(files[i])->type()));
	copy_thread.start();
}

bool RequestHandle::event(QEvent* event_)
{
	switch(event_->type())
	{
	case EVENT_TYPE_COPY_PROGRESS:
		emit copyProgress(static_cast<CopyProgressEvent*>(event_)->prog_report);
		break;
	case EVENT_TYPE_COPY_START:
		emit copyStart(
			copy_thread.files[static_cast<CopyEvent*>(event_)->curr_index].fromPath(),
			copy_thread.files[static_cast<CopyEvent*>(event_)->curr_index].toPath(),
			copy_thread.files[static_cast<CopyEvent*>(event_)->curr_index].fileType());
		break;
	case EVENT_TYPE_ALL_COPY_START:
		emit allCopyStart();
		break;
	case EVENT_TYPE_ALL_COPY_END:
		emit allCopyEnd();
		break;
	case EVENT_TYPE_COPY_ERROR:
		{
			uint32 file_type=copy_thread.files[copy_thread.curr_index].fileType();
			static_cast<CopyErrorEvent*>(event_)->error_report.setExtraInfo(&file_type);
			emit error(static_cast<CopyErrorEvent*>(event_)->error_report,REQUESTHANDLE);
		}
		break;
	case EVENT_TYPE_COPY_CANCEL:
		{
			uint32 file_type=copy_thread.files[copy_thread.curr_index].fileType();
			emit error(ErrorReport(ERROR_TYPE_COPY_CANCEL,0,&file_type),REQUESTHANDLE);
		}
		break;
	default:
		copy_thread.watch_thread.wait_con.wakeAll();
	}
	return true;
}

void RequestHandle::receiveErrorResult(const ErrorHandleResult& re)
{
	if(re.receiver()!=REQUESTHANDLE)
		return;
	switch(re.errorReport().type())
	{
	case ERROR_TYPE_COPY_CANCEL:
		handleCancelCopyResult(static_cast<const CancelCopyResult&>(re));
		break;
	case ERROR_TYPE_WRITEFILE:
		handleWriteErrorResult(static_cast<const CopyErrorResult&>(re));
		break;
	case ERROR_TYPE_CREATEFILE:
		handelCreateErrorResult(re);
		break;
	default:
		;
	}
}

void RequestHandle::handleCancelCopyResult(const CancelCopyResult& re)
{
	if(re.yesOrNo())
	{
		uint32 index=copy_thread.curr_index;
		if(re.isCancelAll())
		{
			if(re.isDeleteAll())
				deleteCopyedFiles();
			copy_thread.is_stop=true;
		}
		copy_thread.watch_thread.is_cancel_copy=true;
		copy_thread.watch_thread.is_cancel_other=re.isCancelOther();
		copy_thread.watch_thread.is_del_file=re.isDeleteFile();
		copy_thread.watch_thread.wait_con.wakeAll();
		if(copy_thread.files[index].fileType()==DIR_ENTRY_FILE_TYPE_DIR&&re.isDeleteDir())
		{
			while(copy_thread.isRunning()&&index==copy_thread.curr_index);
			deleteDir(copy_thread.files[index].toPath());
		}
	}
	else
	{
		copy_thread.watch_thread.is_cancel_copy=false;
		copy_thread.watch_thread.wait_con.wakeAll();
	}
}
void RequestHandle::handleWriteErrorResult(const CopyErrorResult& re)
{
	if(re.yesOrNo())
	{
		uint32 index=copy_thread.curr_index;
		if(re.isDeleteFile())
			copy_thread.watch_thread.is_retry=NOT_RETRY_AND_DEL_FILE;
		else
			copy_thread.watch_thread.is_retry=NOT_RETRY;
		copy_thread.watch_thread.wait_con.wakeAll();
		if(copy_thread.files[index].fileType()==DIR_ENTRY_FILE_TYPE_DIR&&re.isDeleteDir())
		{
			while(copy_thread.isRunning()&&index==copy_thread.curr_index);
			deleteDir(copy_thread.files[index].toPath());
		}
	}
	else
	{
		copy_thread.watch_thread.is_retry=RETRY;
		copy_thread.watch_thread.wait_con.wakeAll();
	}
}
void RequestHandle::handelCreateErrorResult(const ErrorHandleResult& re)
{
	if(re.errorReport().code()==ERROR_FILE_EXISTS||re.errorReport().code()==ERROR_ALREADY_EXISTS)
	{
		copy_thread.watch_thread.is_overwrite=re.yesOrNo();
		copy_thread.watch_thread.wait_con.wakeAll();
	}
	else
	{
		copy_thread.watch_thread.is_overwrite=false;
		copy_thread.watch_thread.wait_con.wakeAll();
	}
}
void RequestHandle::deleteDir(const QString& dir_name)
{
	QDir dir(dir_name);
	dir.setFilter(QDir::Files);
	uint32 count=dir.count();
	for(uint32 i=0;i<count;i++)
		dir.remove(dir[i]);
	dir.setFilter(QDir::AllDirs|QDir::NoDotAndDotDot);
	count=dir.count();
	QString path=dir.absolutePath();
	for(uint32 i=0;i<count;i++)
		deleteDir(path+STR_SLASH+dir[i]);
	QDir parent(path.section(STR_SLASH,0,-2));
	parent.rmdir(path);
}
void RequestHandle::deleteCopyedFiles()
{
	for(uint32 i=0;i<copy_thread.curr_index;i++)
		if(copy_thread.files[i].fileType()==DIR_ENTRY_FILE_TYPE_NORMAL)
			QFile::remove(copy_thread.files[i].toPath());
		else if(copy_thread.files[i].fileType()==DIR_ENTRY_FILE_TYPE_DIR)
			deleteDir(copy_thread.files[i].toPath());
}

void CopyThread::run()
{
	is_finished=false;
	curr_index=0;
	watchStart();
	QApplication::postEvent(parent,new CopyEvent(0,EVENT_TYPE_ALL_COPY_START));
	while(true)
	{
		QApplication::postEvent(parent,new CopyEvent(curr_index,EVENT_TYPE_COPY_START));
		if(!ExtFsDLL::copy(files[curr_index]))
		{
			ErrorReport error_report;
			ExtFsDLL::getErrorReport(error_report);
			if(error_report.type()==ERROR_TYPE_UNKNOWN)
				QApplication::postEvent(parent,new CopyErrorEvent(error_report,EVENT_TYPE_COPY_ERROR));
		}
		if(is_stop)
		{
			is_finished=true;
			break;
		}
		lock_for_files.lock();
		curr_index++;
		if(curr_index>=files.size())
		{
			is_finished=true;
			lock_for_files.unlock();
			break;
		}
		lock_for_files.unlock();
	}
	QApplication::postEvent(parent,new CopyEvent(0,EVENT_TYPE_ALL_COPY_END));
	watchEnd();
}
void CopyThread::watchStart()
{
	ExtFsDLL::startWatching();
	watch_thread.start();
}
void CopyThread::watchEnd()
{
	watch_thread.terminate();
	watch_thread.wait();
	ExtFsDLL::endWatching();
}

void WatchThread::run()
{
	while(true)
	{
		ExtFsDLL::watching();
		switch(ExtFsDLL::getReportType())
		{
		case REPORT_TYPE_PROGRESS:
			handleProgressReport();
			break;
		case REPOTR_TYPE_ERROR:
			handleErrorReport();
			break;
		default:;
		}
	}
}
void WatchThread::handleErrorReport()
{
	ErrorReport error_report;
	QMutex mutex;
	ExtFsDLL::getErrorReport(error_report);
	switch(error_report.type())
	{
	case ERROR_TYPE_WRITEFILE:
		QApplication::postEvent(parent,new CopyErrorEvent(error_report,EVENT_TYPE_COPY_ERROR));
		wait_con.wait(&mutex);
		ExtFsDLL::retry(is_retry);
		break;
	case ERROR_TYPE_COPY_CANCEL:
		QApplication::postEvent(parent,new CopyErrorEvent(error_report,EVENT_TYPE_COPY_CANCEL));
		wait_con.wait(&mutex);
		if(is_cancel_copy)
			ExtFsDLL::cancelCopy(is_cancel_other,is_del_file);
		else
			ExtFsDLL::continueCopy();
		break;
	case ERROR_TYPE_CREATEFILE:
		QApplication::postEvent(parent,new CopyErrorEvent(error_report,EVENT_TYPE_COPY_ERROR));
		wait_con.wait(&mutex);
		ExtFsDLL::overwrite(is_overwrite);
		break;
	case ERROR_TYPE_READFILE:
		QApplication::postEvent(parent,new CopyErrorEvent(error_report,EVENT_TYPE_COPY_ERROR));
		wait_con.wait(&mutex);
		ExtFsDLL::retry(is_retry);
		break;
	default:;
	}
}
void WatchThread::handleProgressReport()
{
	ProgressReport prog_report;
	ExtFsDLL::getProgressReport(prog_report);
	QApplication::postEvent(parent,new CopyProgressEvent(prog_report,EVENT_TYPE_COPY_PROGRESS));
	ExtFsDLL::stopWatching();
}